#ifndef _NetworkInterfaceStructs_h_
#define _NetworkInterfaceStructs_h_

#include <buildspec.h>
#include <config.h>
#include <GSTenums.h>
#include <Geometry/SRS.h>
#include <Geometry/IGeometry.h>
#include <GSTCore/GST_Types.h>
#include <Utils/GSTVersion.h>
#include <transport/JsonFileEncoder.h>

#include <Client/ClientUtils/Network/ColorMap.hpp>
#include <Client/ClientUtils/Network/NetworkInterfaceExceptions.hpp>
#include <Client/ClientUtils/Network/TableAttributeVal.h>
#include <Client/ClientUtils/Network/SimplexPropertyDesc.h>

#include <string>
#include <set>

#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>
#include <boost/ptr_container/ptr_vector.hpp>

namespace GST {

//prototypes:
namespace ClientUtils {
	class IDataTable;
	class FeatureClassDetailedDesc;
	class GeoColumnDesc;
	struct FeatureDesc;
	class FeatureClassDesc;
}
namespace Geometry {
	class ClientGeometry;
}

namespace ClientUtils
{
	using GST::GSTCore::GSTbox;

//helpers:
struct GST_API_EXPORT Host
{
	std::string ip;
	long port;
	std::string user, pwd;
	std::string sslCert;
	Host(const std::string& _url="", const long& _port=0, const std::string& _usr="", const std::string& _pwd="", const std::string & _sslCert="")
		: ip(_url), user(_usr), pwd(_pwd), sslCert(_sslCert)
	{
		port = _port>0 ? _port : 80;
	}
	void setParameters(const std::string& _url="", const long& _port=0, const std::string& _usr="", const std::string& _pwd="", const std::string & _sslCert="")
	{
		ip = _url;
		port = _port;
		user = _usr;
		pwd = _pwd;
		sslCert = _sslCert;
	}
	bool operator ==(const Host& other) const;
	bool operator !=(const Host& other) const;
	bool isAuth() const
	{
		return !user.empty();
	}
}; // struct Host
typedef boost::shared_ptr<Host> HostPtr;

struct GST_API_EXPORT KeyValueList
{
	typedef std::map<std::string, std::string> KVL;
	KVL list;
	const std::string & getKey(const KVL::const_iterator & itr) { return itr->first; }
	const std::string & getValue(const KVL::const_iterator & itr) { return itr->second; }
};
typedef boost::shared_ptr<KeyValueList> KeyValueListPtr;

struct GST_API_EXPORT ServerInfo : public KeyValueList
{
	typedef KeyValueList::KVL::const_iterator const_iterator;

	ServerInfo() {}
	ServerInfo(const std::string& field, const std::string& value)
	{
		list[field] = value;
	}
	void push_back(const std::string& field, const std::string& value)
	{
		list[field] = value;
	}
	const_iterator begin() const { return this->list.begin(); }
	const_iterator end() const { return this->list.end(); }
	size_t size() const { return this->list.size(); }
	static std::string getField(const const_iterator& i) { return i->first; }
	static std::string getValue(const const_iterator& i) { return i->second; }
};
typedef boost::shared_ptr<ServerInfo> ServerInfoPtr;

enum class ZAxisDomain
{
	// The z-axis is height (unit=m), positive direction is up (elevation)
	Height,
	// The z-axis is time (unit=ms), positive direction is down (depth)
	Time,
};

enum class FeatureClassTag
{
	// no special tagging
	Default,
	// 2d segy, mostly applicable to feature classes of lines with float vector
	// properties
	Segy2D,
	// 3d segy, mostly applicable to feature classes of grids
	Segy3D,
	// A geometry with texture coordinates and texture.
	TexturedGeometry,
	// An elevation Grid.
	ElevationGrid,
};

enum class ColorMapOutputFormat
{
	XCMap,
	StratMap
};

enum class Unit
{
	Feet,
	Meters,
	Kilometers,
	Miliseconds,
	Seconds,
};

// see SEGY standard page 7
// https://seg.org/Portals/0/SEG/News%20and%20Resources/Technical%20Standards/seg_y_rev2_0-mar2017.pdf
enum class NumberFormat
{
	IBM4B=1,
	COMP4B,
	COMP2B,
	FIX4B,
	IEEE4B,
	IEEE8B,
	COMP3B,
	COMP1B,
	COMP8B,
	UINT4,
	UINT2,
	UINT8,
	UINT3,
	UINT1,
};

// This enum exists to make `SEGYInfo` dimensionality less prone to error.
enum class Dimensionality
{
	TwoD=2,
	ThreeD,
	FourD,
};

// This enum exists to import `GSTOrderTraceBy` into this part of GST3.
enum class GSTOrderTraceBy
{
  Default = 1,
  TraceSequenceOnLine,
  TraceSequenceInFile,
  FieldRecordNo,
  TraceNo,
  TraceNoInEnsemble,
};

// Trace header as a binary struct, here for performance and import reasons.
class GST_API_EXPORT SEGYTraceHeader {
public:
	SEGYTraceHeader();
	std::string StringifySEGYTraceHeader(void);

	int32_t trace_sequence_on_line, trace_sequence_in_file, field_record_no,
	trace_no, energy_source_point_no, ensemble_no, trace_no_in_ensemble,
	trace_identification_code, no_v_summed_traces, no_h_stacked_traces, data_use,
	source_to_receiver_distance, elevation_of_receiver_group,
	surface_elevation_of_source, source_depth, datum_elevation_of_receiver_group,
	datum_elevation_of_source, water_column_height_at_source,
	water_column_height_at_group, elevation_scalar, coordinate_scalar, source_x,
	source_y, receiver_group_x, receiver_group_y, coordinate_units,
	weathing_velocity, sub_weathering_velocity, uphole_time_at_source,
	uphole_time_at_group, source_static_correction, group_static_correction,
	total_static_applied, lag_time_a, lag_time_b, delay_recording_time,
	mute_time_start, mute_time_end, no_samples_in_trace, sample_interval_of_trace,
	gain_type, instrument_gain_constant, instrument_initial_gain, correlated,
	sweep_frequency_at_start, sweep_frequency_at_end, sweep_length, sweep_type,
	sweep_trace_taper_length_at_start, sweep_trace_taper_length_at_end, taper_type,
	 alias_filter_frequency, alias_filter_slope, notch_filter_frequency,
	 notch_filter_slope, low_cut_frequency, high_cut_frequency, low_cut_slope,
	 high_cut_slope, year_recorded, day_of_year, hour_of_day, minute_of_hour,
	 second_of_minute, time_base_code, trace_weighting_factor,
	 geophone_group_number_roll_pos1, geophone_group_number_first_trace_orig_field,
	 geophone_group_number_last_trace_orig_field, gap_size, over_travel,
	 x_ensemble, y_ensemble, inline_no, crossline_no, shot_point_no,
	 shot_point_scalar, trace_value_measurement_unit,
	 transduction_constant_mantissa, transduction_constant_power,
	 transduction_units, trace_identifier, time_scalar_trace_header, source_type,
	 source_energy_direction_v, source_energy_direction_il,
	 source_energy_direction_xl, source_measurement_mantissa,
	 source_measurement_exponent, source_measurement_unit;
};
typedef boost::shared_ptr<SEGYTraceHeader> SEGYTraceHeaderPtr;

// TODO: may move this to network structs
class GST_API_EXPORT SEGYInfo {

public:
  SEGYInfo();

  int32_t samplesZ, inlines, crosslines;
  double originX, originY, originZ, inlineStepX, inlineStepY, crosslineStepX,
      crosslineStepY, inlineExtremityX, inlineExtremityY, crosslineExtremityX,
      crosslineExtremityY, stepZ;

  ZAxisDomain domain;
  Unit areaUnit, depthUnit;

  uint32_t lineByteLocation, traceByteLocation, currentTraceHeader;
  int32_t inlineOrigin, inlineEnd, crosslineOrigin, crosslineEnd;
  uint32_t CDPX, CDPY;

  NumberFormat sampleFormat, coordinateFormat;
  bool trwf, propIsSigned, missingTraces, littleEndian;
  double scalco;

  std::string textHeader;
	std::string binHeader;
	std::vector<SEGYTraceHeaderPtr> traces;

  std::string filePath;
  std::string defaultPropName;
	std::string navigation_file_path;

	Dimensionality dimensionality;
	GSTOrderTraceBy order_trace_by;
	bool override_to_le, override_trace_format, override_coordinate_format,
	override_coordinate_scaling, override_grid_min_max, override_dim_x,
	override_dim_y, override_dim_z, use_navigation_file, override_o_u_v_w,
	override_units;
};

typedef boost::shared_ptr<SEGYInfo> SEGYInfoPtr;
static const std::string S_M_TRACE_MAX_LENGTH = "m_trace_max_length";
static const std::string S_M_SAMPLE_INTERVAL = "m_sample_interval";
static const std::string S_TRACE_DATA = "trace_data";
static const std::string S_TEX_U = "tex_u";
static const std::string S_TEX_V = "tex_v";

// TODO: we might replace this in the future
class GST_API_EXPORT NamedAttributeIn
{
public:
	std::string name;
	std::string value;
};

class GST_API_EXPORT ParsedFeatureHeader
{
public:
	ParsedFeatureHeader() : name(std::string("")), color(),
		feature_type(GST::Geometry::GeometryTypes::unkown),
		attributes(std::vector<NamedAttributeIn>()),
		properties(std::vector<SimplexPropertyDescPtr>()),
		startPos(std::streampos(-1)), endPos(std::streampos(-1)) {}

	std::string name;
	Geometry::IGeometry::Color color;
	float alpha;
	GST::Geometry::GeometryTypes feature_type;
	std::vector<NamedAttributeIn> attributes;
	std::vector<SimplexPropertyDescPtr> properties;

	std::streampos startPos;
	std::streampos endPos;

};
typedef boost::shared_ptr<ParsedFeatureHeader> ParsedFeatureHeaderPtr;
typedef std::vector<ParsedFeatureHeaderPtr> ParsedFeatureHeaderPtrList;
typedef boost::shared_ptr<ParsedFeatureHeaderPtrList> ParsedFeatureHeaderPtrListPtr;
struct GST_API_EXPORT IfcParserSettings
{
	double h_relative_deviation, h_absolute_deviation, v_relative_deviation,
		v_absolute_deviation, ifc_step_constant;
};
typedef boost::shared_ptr<IfcParserSettings> IfcParserSettingsPtr;

class GST_API_EXPORT FeatureClassDesc
{

protected:
	std::string name;
	std::string owner;
	std::string geometryColumn;
	Geometry::IGeometry::GeometryType geomtype;
	Geometry::SRSPtr srs;
	int numberOfFeatures;
    long long id;
	ZAxisDomain zAxisDomain;
	std::set<FeatureClassTag> tags;
public:

    FeatureClassDesc();
	FeatureClassDesc(const std::string &fullClassName,
					 const std::string &geometryColumn,
					 const Geometry::IGeometry::GeometryType &geomtype,
					 Geometry::SRSPtr srs = Geometry::SRSPtr(),
					 int numberOfFeatures = -1,
					 const long long id = -1,
					 ZAxisDomain zAxisDomain = ZAxisDomain::Height,
					 std::set<FeatureClassTag> tag = {FeatureClassTag::Default});
	FeatureClassDesc(const std::string &name,
					 const std::string &owner,
					 const std::string &geometryColumn,
					 const Geometry::IGeometry::GeometryType &geomtype,
					 Geometry::SRSPtr srs = Geometry::SRSPtr(),
					 int numberOfFeatures = -1,
					 const long long id = -1,
					 ZAxisDomain zAxisDomain = ZAxisDomain::Height,
					 std::set<FeatureClassTag> tag = {FeatureClassTag::Default});

	///@name operators
	//@{
	bool contains(const FeatureDesc& feature) const;
	bool operator ==(const FeatureClassDesc& other) const;
	bool operator !=(const FeatureClassDesc& other) const;
	//@}

	///@name getters/setters
	//@{
	bool isPublic() const;
	std::string getFullName() const;
	std::string getGroup() const;
	std::string getGeometryColumn() const;
	void setGeometryColumn(std::string val);
	Geometry::IGeometry::GeometryType getGeomtype() const;
	void setGeomtype(Geometry::IGeometry::GeometryType val);
	std::string getOwner() const;
	void setOwner(std::string val);
	std::string getName() const;
	void setName(std::string val);
	GST::Geometry::SRSPtr getSrs() const;
	void setSrs(GST::Geometry::SRSPtr val);
	/**
	  * @returns optional member FeatureClassDesc::numberOfFeatures that counts the features in a feature class.
	  * The counter is set by NetworkInterface::ListFeatureClasses().
	  * You can use FeatureClassDesc::isNumberOfFeaturesFetched() to check whether the counter is valid or not
	  */
	int getNumberOfFeatures() const;
	void setNumberOfFeatures(int val);
	/**
	  * @returns true if the counter numberOfFeatures is fetched from the database
	  */
	bool isNumberOfFeaturesFetched() const;
    void setID(const long long& val);
    long long getID() const;
	ZAxisDomain getZAxisDomain() const;
	void setZAxisDomain(ZAxisDomain zAxisDomain);
	std::set<FeatureClassTag> getTags() const;
	void setTags(const std::set<FeatureClassTag> tags);
	//@}
};
typedef boost::shared_ptr<FeatureClassDesc> FeatureClassDescPtr;
typedef boost::ptr_vector<FeatureClassDesc> FeatureClassDescList;
typedef boost::shared_ptr<FeatureClassDescList> FeatureClassDescListPtr;


struct GST_API_EXPORT LockState
{
	enum LockType{FREE=2,OBJECT=4,PART=16};
	LockType state;

	LockState(const LockType & state)
		: state(state)
	{}
};
typedef boost::shared_ptr<LockState> LockStatePtr;

struct GST_API_EXPORT IndexState
{
	enum IndexStateType{IndexInvalid=-1, IndexUnavailable=0, IndexAvailable=1};
	IndexStateType state;

	IndexState(const IndexStateType & state)
		: state(state)
	{}
};
typedef boost::shared_ptr<IndexState> IndexStatePtr;

struct GST_API_EXPORT AccessLevel
{
	friend class boost::serialization::access;

	enum AccessLevelVal
	{
		Public = 0,
		Partner,
		Private,
		None = 999
	};
	AccessLevelVal level;
	std::string desc;

	AccessLevel(const long &lvl)
	{
		switch(lvl)
		{
		case 0:
			this->desc = "Public";
			break;
		case 1:
			this->desc = "Partner";
			break;
		case 2:
			this->desc = "Private";
			break;
		case 999:
			this->desc = "None";
			break;
		default:
			throw GST::exceptions::BadParameter(
				__METHOD_NAME__, "Unknown Access Level", "lvl");
		}
		this->level = (AccessLevelVal)lvl;
	}

private:
	template<typename Archive>
	void serialize(Archive &ar, const unsigned int version)
	{
		// If you change members here do not forget to increment object version
		// (see bottom of this file)
		ar &boost::serialization::make_nvp("desc", desc);
		ar &boost::serialization::make_nvp("level", level);
	}
};

typedef boost::shared_ptr<AccessLevel> AccessLevelPtr;
typedef std::vector<AccessLevelPtr> AccessLevelList;
typedef boost::shared_ptr<AccessLevelList> AccessLevelListPtr;

struct GST_API_EXPORT ServiceDescription
{
	long id;
	std::string name;
	std::string url;
	long accessLevelId;
	std::string user;
	std::string secret;
};

typedef boost::shared_ptr<ServiceDescription> ServiceDescriptionPtr;
typedef std::vector<ServiceDescription> ServiceDescrionList;
typedef boost::shared_ptr<ServiceDescrionList> ServiceDescrionListPtr;

enum class GeometryHullState {
	Unknown,
	Set,
	NotSet
};

enum class TextureState {
	Unknown,
	Set,
	NotSet
};

struct GST_API_EXPORT FeatureDesc
{
	std::string name;
	int levelsAvaiable;
	long geometryId;
	FeatureClassDesc fclass;
	///@name optional information
	//@{
	LockStatePtr lockstate;
	GSTbox bbox;
	IndexStatePtr indexstate;
	GeometryHullState hullstate;
	TextureState texturestate;
	//@}

	FeatureDesc() {}
	FeatureDesc(const std::string& name, const int& levelsAvaiable, const long& geometryId, const FeatureClassDesc& fclass, const GSTbox& bbox);

	///@name operators
	//@{
	bool operator ==(const FeatureDesc& other) const;
	bool operator !=(const FeatureDesc& other) const;
	//@}

	//typedef boost::shared_ptr<ICondition> ConditionPtr;
	//std::vector<ConditionPtr> conditions;
};
typedef boost::shared_ptr<FeatureDesc> FeatureDescPtr;
typedef boost::ptr_vector<FeatureDesc> FeatureDescList;
typedef boost::shared_ptr<FeatureDescList> FeatureDescListPtr;

/**
	* Wrapper for the resulting data (get requests).
	*/
struct GST_API_EXPORT CrudeFeature
{

	FeatureDescPtr desc;
	boost::shared_array<unsigned char> binGeo;
	Geometry::IGeometry::RequestFormat geometryformat;
	Geometry::SRSPtr srs;
	Transport::GeometryData metaData;

	virtual std::string GetGeometry() = 0;
	virtual std::string GetPropertyDesc();
	virtual std::string GetPropertyVals() = 0;
	virtual std::string GetLockId() = 0;
	virtual Transport::GeometryData GetMetaData() const { return this->metaData; }
	virtual void parseMetaData( const std::string input );
	virtual boost::shared_array<unsigned char> GetBinaryGeometry() { return this->binGeo; }
	virtual TableAttributeValListPtr GetObjProperties() = 0;
};
typedef boost::shared_ptr<CrudeFeature> CrudeFeaturePtr;

struct GST_API_EXPORT QueryBox
{
	GSTCore::GSTbox box;
	double borderSize;
	Geometry::SRSPtr srs;

	QueryBox(const GSTCore::GSTbox& box, const double& borderSize, Geometry::SRSPtr srs)
		: box(box)
		, borderSize(borderSize)
		, srs(srs)
	{}

	std::string GetCommaSeparatedMinMaxList() const;
	std::string GetBorderSize() const;
	Geometry::SRSPtr GetSRS() const;
};
typedef boost::shared_ptr<QueryBox> QueryBoxPtr;

/**
	* Configuration for the database. Inherit from here to make
	* db type specific configuration.
	*
	* @todo may merge in future releases with Host and DB name/service name
	*/
struct GST_API_EXPORT DBConfig
{
	int max_reconnect_tries;
	bool operator ==(const DBConfig& other) const;
	bool operator !=(const DBConfig& other) const;

	virtual bool doCompare(const DBConfig& other) const;

	virtual ~DBConfig() {}
};
typedef boost::shared_ptr<DBConfig> DBConfigPtr;

struct GST_API_EXPORT EndPoint
{
	HostPtr host;
	DBConfigPtr dbconfig;
	Protocol protocol;
	std::string database;

	bool operator==( const EndPoint & other ) const;
	bool operator!=(const EndPoint& other) const;
};
typedef boost::shared_ptr<EndPoint> EndPointPtr;
// Creates a deep copy of ep, that doesn't share host/dbconfig ptrs of ep.
EndPoint deepCopy(const EndPoint &ep);

struct GST_API_EXPORT LockInfo
{
	std::string lockId;
	std::string user;
	std::string checkedOut;
	GST::LockType ltype;
};
typedef boost::shared_ptr<LockInfo> LockInfoPtr;
typedef std::vector<LockInfoPtr> LockInfoList;
typedef boost::shared_ptr<LockInfoList> LockInfoListPtr;

struct GST_API_EXPORT SurfacePoint
{
	double x;
	double y;
	bool valid;

	SurfacePoint();
	SurfacePoint(const double & px, const double & py);

	SurfacePoint & operator=(const SurfacePoint & other);
};

struct GST_API_EXPORT SurfaceLine
{
	SurfacePoint p1;
	SurfacePoint p2;

	SurfaceLine();
	SurfaceLine(const SurfacePoint & p1, const SurfacePoint & p2);

	SurfaceLine & operator=(const SurfaceLine & other);
};

//-----------------------------------------------------------------------------------------------------------
/**
  * @brief	stores result of one of the intersection function
  *
  * @param	intersecionImageFileName	file name including path
  *                                     to resulting intersection image
  *
  * @param	legendImageFileName			file name including path
  *                                     to resulting legend image
  *										(optional) only if separate legend image
  *										was requested, empty otherwise
  *
  * @param	legendInfoJson				legend info in json format
  *
  * @see		NetworkInterface::SaveBoreholeImage
  * @see		NetworkInterface::SaveSectionImage
  * @see		NetworkInterface::SaveMapImage
  */
struct GST_API_EXPORT IntersectionInfo
{
	std::string intersecionImageFileName;
	std::string legendImageFileName;
	std::string legendInfoJson;
};
typedef boost::shared_ptr<IntersectionInfo> IntersectionInfoPtr;

struct GST_API_EXPORT PropertyAlias
{

	friend class boost::serialization::access;

public:
	std::string aliasName;
	AccessLevel accessLevel;
	bool isConstrained;

	PropertyAlias(const std::string &aliasName, AccessLevel accessLevel, const bool& isConstrained)
		: aliasName(aliasName)
		, accessLevel(accessLevel)
		, isConstrained(isConstrained)
	{

	}

	///default constructor for boost serialization support
	PropertyAlias()
		: aliasName("undefined")
		, accessLevel(AccessLevel::Public)
		, isConstrained(false)
	{

	}

private:
	template <typename Archive>
	void serialize(Archive &ar, const unsigned int version)
	{
		//If you change members here do not forget to increment object version (see bottom of this file)
		ar & boost::serialization::make_nvp("aliasName",aliasName); 
		ar & boost::serialization::make_nvp("accessLevel",accessLevel);
		ar & boost::serialization::make_nvp("isConstrained",isConstrained);
	} 
};
typedef boost::shared_ptr<PropertyAlias> PropertyAliasPtr;
//key: property name; value: alias
typedef std::map<std::string, std::map<int, PropertyAliasPtr>, Utils::UpperCaseCompare> PropertyAliasMap;
typedef boost::shared_ptr<PropertyAliasMap> PropertyAliasMapPtr;

typedef std::pair<std::string, std::string> webLogin;
struct GST_API_EXPORT dbLogin
{
    std::string user;
    std::string pw;
    std::string allowUpload;
    std::string allowSHPSection;
    long accessLevel;
    dbLogin()
        : user()
        , pw()
        , allowUpload()
        , accessLevel(-1)
        , allowSHPSection()
    {
    }
};
struct GST_API_EXPORT WebUserDesc
{
    long id;
    std::string login;
    std::string allowUpload;
    std::string allowSHPSection;
    long accessLevel;
    WebUserDesc(long id,
                const std::string &login,
                const std::string &allowUpload,
                const std::string &allowSHPSection,
                long accessLevel)
        : id(id)
        , login(login)
        , allowUpload(allowUpload)
        , allowSHPSection(allowSHPSection)
        , accessLevel(accessLevel)
    {
    }
};
typedef std::vector<WebUserDesc> WebuserDescList;
typedef boost::shared_ptr<WebuserDescList> WebuserDescListPtr;
//typedef std::tuple<std::string, std::string, std::string> dbLogin;
typedef std::tuple<std::string, std::string, std::string> webToDbMapping;
typedef std::vector<webToDbMapping> webToDbMappingList;

struct GST_API_EXPORT FeatureData
{
	FeatureDescPtr featureDesc;
	LockInfoListPtr lockInfos;
	TableAttributeValListPtr objectPropertyValues;
	bool hasGeometryHull;
};
typedef std::vector<FeatureData> FeatureDataList;

} //namespace ClientUtils
} //namespace GST

BOOST_CLASS_EXPORT_KEY(GST::ClientUtils::PropertyAlias);

BOOST_CLASS_VERSION(GST::ClientUtils::PropertyAlias, 3)

BOOST_CLASS_EXPORT_KEY(GST::ClientUtils::AccessLevel);

BOOST_CLASS_VERSION(GST::ClientUtils::AccessLevel, 2)

#endif // _NetworkInterfaceStructs_h_
